-- DungeonLabM
-- Created by chengb Apr/25/2015
-- 死灵实验台

module("DungeonLabM", package.seeall);

-- 实验台配置
local dungeonLabTable = {};
local labList = {};

local SKY_LAB_ITEM_ID = 8162;
local MAX_HARVEN_RING_LEVEL = 10;

-- 内部函数声明
local whenLeaveDungeon;

-- 载入配置表
local _init = false;
local loadCsv = function()
    if _init then return end
    _init = true;

    -- 载入配置表
    local dungeon_lab_CSV = get_dungeon_lab_CSV();
    for i = 1, #dungeon_lab_CSV do
        local id = dungeon_lab_CSV[i].id;
        dungeonLabTable[id] = dungeon_lab_CSV[i];
    end

    -- 根据id排序
    labList = table.values(dungeonLabTable, function(a, b) return a.id < b.id; end);
end

-- 初始化
function init()
    loadCsv();

    -- 关注玩家离开地牢的事件
    EventMgr.removeAll("DungeonLabM");
    EventMgr.register("DungeonLabM", event.LEAVE_DUNGEON, function()
        whenLeaveDungeon();
    end);
end

-- 检索配置
function query(id, path)
    local m = dungeonLabTable[id]

    if not m or path == "" or not path then
        return m;
    end

    return m[path];
end

-- 抽取实验台id
function fetchLabId()
    -- 按固定顺序出现
    local id = FormulaM.invoke("CALC_LAB_ORDER", ME.user)
    local msg = string.format("[DungeonLabM]抽取实验台(%d)", id);
    DungeonLogM.addLog(msg);

    return id;
end

-- 获取当前拥有的装备id
function getCurEquipId(classId)
    local equips = ItemM.getItemsByType(ME.user, ITEM_TYPE_SPECIAL_TREASURE);
    for _, id in pairs(equips) do
        if ItemM.getAmount(ME.user, id) > 0 and
           EquipM.findBaseEquip(id) == EquipM.findBaseEquip(classId) then
        	return id;
        end
    end

    -- 如果包裹中没有，可能穿在身上
    return getWearedEquip(classId);
end

-- 获取玩家穿在身上的装备
function getWearedEquip(classId)
    local type = EquipM.query(classId, "type");     -- 装备类型
    local equipments = ME.user.equipments;
    local equiped = equipments[type];

    -- 没有装备
    if not equiped or #equiped <= 0 then
        return nil;
    end

    local pos = 1;
    if not equiped[pos] then
        return nil;
    end

    local id = equiped[pos];
    if EquipM.findBaseEquip(id) ~= EquipM.findBaseEquip(classId) then
        -- 不是同一件装备
        return nil;
    end

    return id;
end

-- 玩家离开地牢的回调
function whenLeaveDungeon()
    if ME.user.dbase:query("lab_upgrade_record") then
        -- 移除升级失败记录
        ME.user.dbase:delete("lab_upgrade_record");
    end

    if ME.user.dbase:query("harven_ring_level") then
        -- 移除骨戒等级信息
        ME.user.dbase:delete("harven_ring_level");
    end
end

-- 合成物品
-- @param pos 实验台所在的格子位置
function doCompound(pos)
    local grid = DungeonM.getGridByPos(pos);

    -- 格子类型不符
    if not grid or grid.type ~= GRID_TYPE_LAB then
        trace("DungeonLabM", "格子%d不是实验台。", pos);
        return false;
    end

    local labId = grid.labId;
    if type(labId) ~= 'number' then
        trace("DungeonLabM", "获取不到实验台id。");
        return false;
    end

    local labInfo = query(labId);
    if not labInfo then
        trace("DungeonLabM", "获取不到实验台(id=%d)的配置。", labId);
        return false;
    end

    local classId = labInfo["class_id"];
    local ruleId = CompoundM.getCompoundRule(classId);
    if true ~= CompoundM.canDoCompound(ruleId) then
        trace("DungeonLabM", "物品(%d)不满足合成条件。", classId);
        return false;
    end

    -- 执行合成
    if true ~= CompoundM.doCompound(ruleId, "lab_compound_item") then
        trace("DungeonLabM", "物品(%d)合成失败了。", classId);
        return false;
    end

    -- 抛出事件
    EventMgr.fire(event.LAB_COMPOUND_ITEM, { ["classId"] = classId, });
    return true;
end

-- 装备升级
-- @param pos 实验台所在的格子位置
function upgradeEquip(pos)
    local grid = DungeonM.getGridByPos(pos);

    -- 格子类型不符
    if not grid or grid.type ~= GRID_TYPE_LAB then
        trace("DungeonLabM", "格子%d不是实验台。", pos);
        return false;
    end

    local labId = grid.labId;
    if type(labId) ~= 'number' then
        trace("DungeonLabM", "获取不到实验台id。");
        return false;
    end

    local labInfo = query(labId);
    if not labInfo then
        trace("DungeonLabM", "获取不到实验台(id=%d)的配置。", labId);
        return false;
    end

    -- 获取初级装备
    local classId = labInfo["class_id"];

    -- 获取当前等级的装备
    local curClassId = getCurEquipId(classId);
    if not curClassId then
        trace("DungeonLabM", "装备(%d)尚未合成，无法升级。", curClassId);
        return false;
    end

    -- 已经满级
    if EquipM.findMaxEquip(classId) == curClassId then
        trace("DungeonLabM", "装备(%d)已经满级，无法升级。", classId);
        return false;
    end

    -- 获取下一级装备
    local nextClassId = EquipM.query(curClassId, "next_class_id");

    local ruleId = CompoundM.getCompoundRule(nextClassId);
    if true ~= CompoundM.canDoCompound(ruleId) then
        trace("DungeonLabM", "升级失败，装备(%d)不满足合成条件。", nextClassId);
        return false;
    end

    -- 判断是否升级成功
    local isSucc = FormulaM.invoke("IS_LAB_UPGRADE_EQUIP_SUCC", ME.user, curClassId);

    local msg = string.format("[DungeonLabM]装备(%d)升级结果：%s", curClassId, tostring(isSucc));
    DungeonLogM.addLog(msg);

    if isSucc then
        -- 扣除旧装备
        if not costOldEquip(curClassId) then
            trace("DungeonLabM", "装备(%d)扣除失败了。", curClassId);
            return false;
        end

        -- 重置失败次数
        resetFailTimes(classId);

        -- 如果成功，执行合成
        CompoundM.doCompound(ruleId, "lab_upgrade_equip");
    else
        -- 失败了，直接扣除材料
        local cost = CompoundM.calcCost(ruleId);
        for _, arr in pairs(cost) do
            -- TODO 目前仅支持物品消耗
            if not ItemM.costAmount(ME.user, arr[2], arr[3]) then
                -- 物品扣除失败了
                return false;
            end
        end

        -- 记录失败次数
        recordFailTimes(classId);
    end

    -- 抛出事件
    EventMgr.fire(event.LAB_UPGRADE_EQUIP, { ["labId"] = labId,  ["classId"] = nextClassId, ["isSucc"] = isSucc, });

    return true;
end

-- 扣除旧装备
function costOldEquip(classId)
    if ItemM.getAmount(ME.user, classId) > 0 then
        -- 装备在包裹中，直接扣除
        if not ItemM.costAmount(ME.user, classId, 1) then
            return false;
        end
    else
        -- 装备穿在身上

        local type = EquipM.query(classId, "type");
        local equipments = ME.user.equipments;
        local equiped = equipments[type];

        -- 没有此类型装备
        if not equiped or #equiped <= 0 or table.indexOf(equiped, classId) < 0 then
            return false;
        end

        local pos = 1;
        if not equiped[pos] then
            return false;
        end

        -- 删除该装备
        table.remove(equiped, pos)
        if #equiped <= 0 then
            equipments[type] = nil;
        end
    end

    return true;
end

-- 记录连续失败次数
function recordFailTimes(classId)
    classId = EquipM.findBaseEquip(classId);
    local record = ME.user.dbase:query("lab_upgrade_record", {});
    if not record[classId] then
        record[classId] = 0;
    end

    record[classId] = record[classId] + 1;
    ME.user.dbase:set("lab_upgrade_record", record);

    -- TODO
    printTable(record, "实验室升级失败记录");
end

-- 重置连续失败次数
function resetFailTimes(classId)
    classId = EquipM.findBaseEquip(classId);
    local record = ME.user.dbase:query("lab_upgrade_record", {});
    if not record[classId] then
        return;
    end

    record[classId] = 0;
    ME.user.dbase:set("lab_upgrade_record", record);
end

-- 获取连续失败次数
function getFailTimes(classId)
    classId = EquipM.findBaseEquip(classId);
    local record = ME.user.dbase:query("lab_upgrade_record", {});
    if not record[classId] then
        return 0;
    end
    return record[classId];
end

-- 获得哈文的骨戒
function getSkyLabItemId()
    return SKY_LAB_ITEM_ID;
end

function isHarvenRing(classId)
    if SKY_LAB_ITEM_ID == classId then
        return true;
    end

    return false;
end

-- 获得哈文骨戒的等级
function getHarvenRingLevel()
    local level = ME.user.dbase:query("harven_ring_level", 0);
    return level;
end

-- 升级哈文的骨戒
function upgradeHarvenRingLevel()
    local maxLevel = MAX_HARVEN_RING_LEVEL;

    local curLevel = getHarvenRingLevel();
    if curLevel >= maxLevel then
        trace("DungeonLabM", "哈文的骨戒(%d)等级已达最高等级。");
        return false;
    end

    -- 检查消耗
    local cost = FormulaM.invoke("CALC_UPGRADE_HARVEN_RING_COST");
    if cost[1] == 1 then
        local itemId = cost[2];
        local amount = cost[3];
        local curAmount = ItemM.getAmount(ME.user, itemId);
        if curAmount < amount then
            trace("DungeonLabM", "无法升级哈文的骨戒的属性，物品(%d)数量不足", itemId);
            return false;
        end

        -- 扣除物品
        ItemM.costAmount(ME.user, itemId, amount);
    else
        assert(false, "暂不支持的消耗类型");
    end
    -- 提升等级
    ME.user.dbase:set("harven_ring_level", curLevel + 1);

    local msg = string.format("[DungeonLabM]哈文的骨戒的等级提升为(%d)", SKY_LAB_ITEM_ID, curLevel + 1);
    DungeonLogM.addLog(msg);

    -- 如果穿在身上，刷新一下附加属性
    if EquipM.isEquiped(SKY_LAB_ITEM_ID) then
        PropM.refresh(ME.user);
    end

    -- 抛出事件
    EventMgr.fire(event.LAB_UPGRADE_EQUIP, {["classId"] = SKY_LAB_ITEM_ID, ["isSucc"] = true, });
    return true;
end

--------------------------
-- 计算哈文的骨戒属性
function getExtraProps(classId, level)
    local ringProp = FormulaM.invoke("CALC_HARVEN_RING_PROPS");

    if type(ringProp) == "table" then
        return ringProp;
    end
    return {};
end

function getHarvenRingMaxLevel()
    return MAX_HARVEN_RING_LEVEL;
end